package com.founder.gateway.component.transport.netty.extension.support;

import java.io.IOException;
import java.util.*;

import org.apache.commons.lang3.StringUtils;
import org.setu.framework.search.SetuGridSearchUtil;
import org.setu.framework.thread.SetuThreadLocalHandler;
import org.setu.framework.util.SetuApplicationUtil;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.founder.gateway.component.transport.netty.exception.NettyException;
import com.founder.gateway.component.transport.netty.filter.NettyFilterChain;
import com.founder.gateway.component.transport.netty.http.NettyHttpRequest;
import com.founder.gateway.component.transport.netty.http.NettyHttpResponse;
import com.founder.gateway.orm.fzapi.bean.entity.ApiReturnParamEntity;
import com.founder.gateway.service.ApiService;
import com.founder.gateway.system.bean.SetuResult;
import com.founder.gateway.system.consts.ApiSetuErrorCode;
import com.founder.gateway.system.consts.Consts;
import com.founder.gateway.system.util.HttpNormUtil;
import com.founder.gateway.system.util.JSONUtil;

public class ResponseParamNettyHttpFilter extends AbstractNettyHttpFilter {

    @Override
    public void doFilter(NettyHttpRequest request, NettyHttpResponse response, NettyFilterChain chain) throws IOException, NettyException {
        chain.doFilter(request, response);
        doResponseParam(response, chain);
    }

    private void doResponseParam(NettyHttpResponse response, NettyFilterChain chain) throws NettyException {
        List<ApiReturnParamEntity> list = SetuApplicationUtil.getBean(ApiService.class).queryApiReturnParam(chain.getApiSchemeEntity().getId());
        if (!list.isEmpty()) {
            try {
                ApiReturnParamEntity entity = list.get(0);

                String codeName = entity.getCodePara();
                String dataName = entity.getDataPara();
                String msgName = entity.getMsgPara();
                codeName = StringUtils.isBlank(codeName) ? Consts.RESULT_CODE_NAME : codeName;
                dataName = StringUtils.isBlank(dataName) ? Consts.RESULT_DATA_NAME : dataName;
                msgName = StringUtils.isBlank(msgName) ? Consts.RESULT_MSG_NAME : msgName;

                SetuResult result = response.getSetuResult();
                if (result == null) {
                    JSONObject json = JSON.parseObject(new String(response.getContent()));
                    result = new SetuResult();
                    result.setRows(json.get(dataName));
                    result.setMsg(JSONUtil.toString(json.get(msgName)));
                    String code = JSONUtil.toString(json.get(codeName));
                    if (code != null) {
                        if (Integer.toString(Consts.RESULT_CODE_FAIL).equals(code) || code.equals(entity.getErrorData())) {
                            result.setCode(Consts.RESULT_CODE_FAIL);
                        }
                    }
                    Object total = json.get(Consts.RESULT_TOTAL_NAME);
                    if (total != null) {
                        try {
                            result.setTotal(Integer.valueOf(total.toString()));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } else {
                        total = SetuThreadLocalHandler.getParam(SetuGridSearchUtil.TOTAL);
                        if (total != null) {
                            result.setTotal(Integer.valueOf(total.toString()));
                        }
                    }
                    response.setSetuResult(result);
                }
                JSONArray filter = JSON.parseArray(entity.getResponse());
                if (filter == null) {
                    throw new NettyException(ApiSetuErrorCode.API_RESPONSE_PARAM_TRANSFER, entity.getDataPara() + HttpNormUtil.DATA_MATCH_ERROR);
                }
                Object rows = result.getRows();
                if (rows != null) {
                    JSONArray array;
                    Object raw = JSON.toJSON(rows);
                    if (raw instanceof JSONArray) {
                        array = (JSONArray) raw;
                    } else {
                        array = new JSONArray();
                        array.add(raw);
                    }
                    result.setRows(filterJson(array, filter));
                }
            } catch (Exception e) {
                throw new NettyException(ApiSetuErrorCode.API_RESPONSE_PARAM_TRANSFER, e.getMessage());
            }
        }
    }

    @SuppressWarnings("unchecked")
    private static <T extends JSON> T filterJson(T s, T t) {
        if (s instanceof JSONArray && t instanceof JSONArray) {
            JSONArray target = (JSONArray) t;
            JSONArray source = (JSONArray) s;
            JSONArray result = new JSONArray();
            for (int i = 0, sourceSize = source.size(), targetSize = target.size(); i < sourceSize; i++) {
                Object itemSource = source.get(i);
                Object itemTarget = null;
                if (i < targetSize) {
                    itemTarget = target.get(i);
                } else if (targetSize > 0) {
                    itemTarget = target.get(targetSize - 1);
                }
                if (itemSource instanceof JSONArray && itemTarget instanceof JSONArray) {
                    result.addAll(filterJson((JSONArray) itemSource, (JSONArray) itemTarget));
                } else if (itemSource instanceof JSONObject && itemTarget instanceof JSONObject) {
                    result.add(filterJson((JSONObject) itemSource, (JSONObject) itemTarget));
                } else if (itemTarget == null) {
                    result.add(itemSource);
                }
            }
            return (T) result;
        } else if (s instanceof JSONObject && t instanceof JSONObject) {
            JSONObject target = (JSONObject) t;
            JSONObject source = (JSONObject) s;
            JSONObject result = new JSONObject();
            for (Map.Entry<String, Object> entry : target.entrySet()) {
                String sourceKey;
                String targetKey;
                String raw = entry.getKey();
                if (raw.contains("|")) {
                    String[] raws = raw.split("\\|");
                    sourceKey = raws[0];
                    targetKey = raws[1];
                } else {
                    sourceKey = raw;
                    targetKey = raw;
                }
                if (source.containsKey(sourceKey)) {
                    Object valueSource = source.get(sourceKey);
                    Object valueTarget = entry.getValue();
                    if (valueSource instanceof JSONArray && valueTarget instanceof JSONArray) {
                        result.put(targetKey, filterJson((JSONArray) valueSource, (JSONArray) valueTarget));
                    } else if (valueSource instanceof JSONObject && valueTarget instanceof JSONObject) {
                        result.put(targetKey, filterJson((JSONObject) valueSource, (JSONObject) valueTarget));
                    } else if (valueTarget == null) {
                        result.put(targetKey, valueSource);
                    }
                } else {
                    result.put(targetKey, null);
                }
            }
            return (T) result;
        }
        return null;
    }
}
